package com.sportradar.assessment.work.services;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sportradar.assessment.work.beans.ConfigurationBean;
import com.sportradar.assessment.work.beans.StatisticsBean;
import com.sportradar.assessment.work.util.ConfigUtil;
import com.sportradar.assessment.work.util.Constants;

public class ExecutionManager {

	private static Logger logger=LoggerFactory.getLogger(ExecutionManager.class);
	
	private NumberProgression numberProgression;
	private Date lastUpdate;
	private ConfigurationBean currentConfiguration;
	private long numberOfExecutions;
	private StatisticsBean statistics;

	public ExecutionManager(NumberProgression numberProgression,ConfigurationBean configuration) {	
		this.numberProgression = numberProgression;
		this.currentConfiguration = configuration;
		this.lastUpdate= new Date();
		this.numberOfExecutions=0;
		this.statistics=new StatisticsBean();
	}
	
	
	public long getNumberOfExecutions() {
		return numberOfExecutions;
	}


	public void setNumberOfExecutions(long numberOfExecutions) {
		this.numberOfExecutions = numberOfExecutions;
	}


	public Date getLastUpdate() {
		return lastUpdate;
	}

	public void setLastUpdate(Date lastUpdate) {
		this.lastUpdate = lastUpdate;
		
	}

	public void updateSettings(){
		
		//1) last update
		logger.info("Last Update was in "+this.getLastUpdate());
		
		//2) Current Update
		Calendar nowAsCalendar = Calendar.getInstance();	
		logger.info("Update now: "+nowAsCalendar.getTime());
		this.setLastUpdate(nowAsCalendar.getTime());
				
		//3) Next Update
		long refreshRateInSeconds=ConfigUtil.getPropertyValueAsLong(Constants.REFRESH_INTERVAL_KEY);
		Calendar nextUpdate =  Calendar.getInstance();	
		nextUpdate.setTime(this.getLastUpdate());
		nextUpdate.add(Calendar.SECOND, (int)refreshRateInSeconds);
		logger.info("next Update will be "+nextUpdate.getTime());
		
		ConfigurationBean newConfiguration=ConfigUtil.update();
		if (!currentConfiguration.getCurrentNumberSequenceThreadName().equals(newConfiguration.getCurrentNumberSequenceThreadName())){
			logger.info("Changed active Thread! New Active ThreadName is "+newConfiguration.getCurrentNumberSequenceThreadName());
		}
		if (!currentConfiguration.getCurrentConfigThreadName().equals(newConfiguration.getCurrentConfigThreadName())){
			logger.info("Changed Config Thread! New Config ThreadName is "+newConfiguration.getCurrentConfigThreadName());
		}
		this.currentConfiguration = newConfiguration;
	}
	
	

	public void execute(String threadName) throws InterruptedException{
		
		while(true){
			if(threadName.equals(currentConfiguration.getCurrentConfigThreadName())){
				TimeUnit.SECONDS.sleep(currentConfiguration.getIntervalBetweenConfigRefresh());
				updateSettings();			
			}else{
				executeCalculation(threadName);
			}
		}
	}
	
	


	public synchronized void executeCalculation(String threadName) throws InterruptedException{
	
		while(!threadName.equals(currentConfiguration.getCurrentNumberSequenceThreadName())){
				wait();
		}
		
		TimeUnit.SECONDS.sleep(currentConfiguration.getIntervalBetweenCalculations());
		long startComputation = System.nanoTime();
		long nextNumber=numberProgression.getNextNumber();
		long endComputation = System.nanoTime();
			
		long computationSpeed=endComputation-startComputation;
		logger.debug("computationSpeed Found:"+computationSpeed); 		
		logger.info(threadName+ ":"+nextNumber);
		
		this.statistics.incrementComputationSpeed(computationSpeed);
		this.statistics.incrementIterationNumber();
		notifyAll();
		
	}


	public StatisticsBean getStatistics() {
		return statistics;
	}
	
	
	
	
	
}
